package tools

import (
	"context"
	"log"
	"os"
	"strconv"
	"time"

	microlog "go-micro.dev/v5/logger"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
	"gorm.io/gorm/logger"
	"gorm.io/plugin/dbresolver"
)

/*
 +----------------------------------------------------------------------
 + Title        : grom
 + Author       : huwl
 + Version      : 1.0.0
 + Initial-Time : 2021-09-22
 + Last-time    : 这个文件最后修改的时间 + 修改人的名称
 + Desc         : 创建grom连接
 +----------------------------------------------------------------------
*/

var DatabaseManager *gorm.DB

// mysql 配置信息
type MysqlConf struct {
	Mode   string
	Single struct {
		Host     string `yaml:"host" json:"host"`
		Port     int64  `yaml:"port" json:"port"`
		DbName   string `yaml:"db_name" json:"db_name"`
		Username string `yaml:"username" json:"username"`
		Password string `yaml:"password" json:"password"`
	}

	Write struct {
		Host     string `yaml:"host" json:"host"`
		Port     int64  `yaml:"port" json:"port"`
		DbName   string `yaml:"db_name" json:"db_name"`
		Username string `yaml:"username" json:"username"`
		Password string `yaml:"password" json:"password"`
	}

	Read struct {
		Host     string `yaml:"host" json:"host"`
		Port     int64  `yaml:"port" json:"port"`
		DbName   string `yaml:"db_name" json:"db_name"`
		Username string `yaml:"username" json:"username"`
		Password string `yaml:"password" json:"password"`
	}
}

// DualLogger 是一个同时输出到两个logger的包装器
type DualLogger struct {
	fileLogger    logger.Interface
	consoleLogger logger.Interface
}

func init() {
	InitMysqlDatabaseConnect()
}

/**
 * 初始化 mysql 数据库连接
 * @author huwl
 * @date 2025/11/07 11:44:50
 */
func InitMysqlDatabaseConnect() {
	var err error
	mysqlConf := &MysqlConf{}
	// ------------- 读取本地配置开始 -------------
	// // 读取database配置
	// cfg, err := GetConfig("database", "yml")
	// if err != nil {
	// 	log.Fatal("读取database配置失败: ", err)
	// }

	// // 组装配置
	// mysqlConf.Mode = cfg.GetString(Env + ".mode")

	// // 单机配置
	// mysqlConf.Single.Host = cfg.GetString(Env + ".single.host")
	// mysqlConf.Single.Port = cfg.GetInt64(Env + ".single.port")
	// mysqlConf.Single.DbName = cfg.GetString(Env + ".single.db_name")
	// mysqlConf.Single.Username = cfg.GetString(Env + ".single.username")
	// mysqlConf.Single.Password = cfg.GetString(Env + ".single.password")

	// // 读配置
	// mysqlConf.Read.Host = cfg.GetString(Env + ".read.host")
	// mysqlConf.Read.Port = cfg.GetInt64(Env + ".read.port")
	// mysqlConf.Read.DbName = cfg.GetString(Env + ".read.db_name")
	// mysqlConf.Read.Username = cfg.GetString(Env + ".read.username")
	// mysqlConf.Read.Password = cfg.GetString(Env + ".read.password")

	// // 写配置
	// mysqlConf.Write.Host = cfg.GetString(Env + ".write.host")
	// mysqlConf.Write.Port = cfg.GetInt64(Env + ".write.port")
	// mysqlConf.Write.DbName = cfg.GetString(Env + ".write.db_name")
	// mysqlConf.Write.Username = cfg.GetString(Env + ".write.username")
	// mysqlConf.Write.Password = cfg.GetString(Env + ".write.password")
	// -------------- 读取本地配置结束 --------------

	// -------------- 读取配置中心开始 --------------
	InitConfigCenter(MySqlConfigCenter, mysqlConf)
	// -------------- 读取配置中心结束 --------------

	var writeDSN string
	var readDSN string
	switch mysqlConf.Mode {
	case "single": // 单机模式
		writeDSN = mysqlConf.Single.Username + ":" + mysqlConf.Single.Password + "@tcp(" + mysqlConf.Single.Host + ":" + strconv.FormatInt(mysqlConf.Single.Port, 10) + ")/" + mysqlConf.Single.DbName + "?charset=utf8mb4&parseTime=True&loc=Local"
	case "rw": // 读写分离模式
		// 写地址
		writeDSN = mysqlConf.Write.Username + ":" + mysqlConf.Write.Password + "@tcp(" + mysqlConf.Write.Host + ":" + strconv.FormatInt(mysqlConf.Write.Port, 10) + ")/" + mysqlConf.Write.DbName + "?charset=utf8mb4&parseTime=True&loc=Local"

		// 读地址
		readDSN = mysqlConf.Read.Username + ":" + mysqlConf.Read.Password + "@tcp(" + mysqlConf.Read.Host + ":" + strconv.FormatInt(mysqlConf.Read.Port, 10) + ")/" + mysqlConf.Read.DbName + "?charset=utf8mb4&parseTime=True&loc=Local"
	}

	// ------------- 使用自定义 mysql logger 开始 -------------
	logPath := "logs/gorm.log"
	err = EnsureDir(logPath)
	if err != nil {
		microlog.Fatal("创建 mysql 日志文件失败: ", err)
	}

	file, err := os.OpenFile(logPath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
	if err != nil {
		microlog.Fatal("无法打开日志文件: ", err)
	}
	// defer file.Close()

	// 文件logger（无颜色）
	fileLogger := logger.New(
		log.New(file, "\r\n", log.LstdFlags),
		logger.Config{
			SlowThreshold: time.Second,
			LogLevel:      logger.Info,
			Colorful:      false,
		},
	)

	// 控制台logger（有颜色）
	consoleLogger := logger.New(
		log.New(os.Stdout, "\r\n", log.LstdFlags),
		logger.Config{
			SlowThreshold: time.Second,
			LogLevel:      logger.Info,
			Colorful:      true, // 控制台使用颜色
		},
	)

	// 创建双logger
	dualLogger := &DualLogger{
		fileLogger:    fileLogger,
		consoleLogger: consoleLogger,
	}
	// ------------- 使用自定义 mysql logger 结束 -------------

	// 连接mysql
	db, err := gorm.Open(mysql.Open(writeDSN), &gorm.Config{
		// 使用默认日志
		// Logger: logger.Default.LogMode(logger.Info),
		// 使用自定义 mysql logger
		Logger: dualLogger,
	})
	if err != nil {
		microlog.Fatal("连接mysql失败: ", err)
	}

	// 读写分离
	if mysqlConf.Mode == "rw" {
		db.Use(dbresolver.Register(dbresolver.Config{
			Replicas: []gorm.Dialector{mysql.Open(readDSN)},
		}))
	}

	sqlDB, err := db.DB()
	if err != nil {
		microlog.Fatal("mysql读写分离失败: ", err)
	}

	// 设置数据库连接池最大连接数
	sqlDB.SetMaxOpenConns(100)

	// 设置空闲连接池中的最大连接数,如果没有sql任务需要执行的连接数大于20，超过的连接会被连接池关闭。
	sqlDB.SetMaxIdleConns(20)

	sqlDB.SetConnMaxLifetime(time.Hour)

	DatabaseManager = db
	log.Println("mysql 连接成功")
}

// LogMode 设置两个logger的日志模式
func (d *DualLogger) LogMode(level logger.LogLevel) logger.Interface {
	newLogger := &DualLogger{
		fileLogger:    d.fileLogger.LogMode(level),
		consoleLogger: d.consoleLogger.LogMode(level),
	}
	return newLogger
}

// Info 实现Info方法
func (d *DualLogger) Info(ctx context.Context, msg string, data ...interface{}) {
	d.fileLogger.Info(ctx, msg, data...)
	d.consoleLogger.Info(ctx, msg, data...)
}

// Warn 实现Warn方法
func (d *DualLogger) Warn(ctx context.Context, msg string, data ...interface{}) {
	d.fileLogger.Warn(ctx, msg, data...)
	d.consoleLogger.Warn(ctx, msg, data...)
}

// Error 实现Error方法
func (d *DualLogger) Error(ctx context.Context, msg string, data ...interface{}) {
	d.fileLogger.Error(ctx, msg, data...)
	d.consoleLogger.Error(ctx, msg, data...)
}

// Trace 实现Trace方法
func (d *DualLogger) Trace(ctx context.Context, begin time.Time, fc func() (string, int64), err error) {
	d.fileLogger.Trace(ctx, begin, fc, err)
	d.consoleLogger.Trace(ctx, begin, fc, err)
}
